package com.ccc.boot.security.util;

import cn.hutool.json.JSONUtil;
import com.ccc.boot.security.constant.AuthConstant;
import com.ccc.boot.security.pojo.Payload;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.SignatureException;
import org.joda.time.DateTime;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

/**
 * JWT工具类
 *
 *  Jwts组成
 *      第一部分为头部header，(base64后的)
 *      第二部分为载荷payload（可以理解为消息主体），(base64后的)
 *      第三部分为签证signature
 *
 * @author superC
 * @version 0.1.0
 * @date 2022/3/23
 */
public class JwtUtils {

    /**
     * 创建JWT的唯一标识
     *
     * @date 2022/4/11 16:52

     * @return {@link String}
     * @author superC
     */
    private static String createJwtId() {
        return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
    }

    /**
     * 私钥加密生成token
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间，单位分钟
     * @return JWT
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
        return Jwts.builder()
                // 注入参数
                .claim(AuthConstant.TOKEN_PAYLOAD_USER_KEY, JSONUtil.toJsonStr(userInfo))
                // 设置JWT ID，作用: 设置为一个不重复的值，用来标识token，回避重放攻击。
                .setId(createJwtId())
                // 设置过期时间
                .setExpiration(DateTime.now().plusMinutes(expire).toDate())
                // 设置算法及密钥
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 私钥加密生成token
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @return      token
     */
    public static String generateToken(Object userInfo, PrivateKey privateKey) {
        return generateTokenExpireInMinutes(userInfo, privateKey, AuthConstant.TOKEN_EXPIRATION_MINUTES);
    }

    /**
     * 公钥解析token
     *
     * @param token 用户请求中的token
     * @return Jws<Claims>
     */
    private static Claims parserToken(String token, PublicKey publicKey) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
    }

    /**
     * 获取token中的用户信息
     *
     * @param token 用户请求中的令牌
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
        Claims claims = parserToken(token, publicKey);
        Payload<T> payload = new Payload<>();
        payload.setId(payload.getId());
        payload.setUserInfo(JSONUtil.toBean(claims.get(AuthConstant.TOKEN_PAYLOAD_USER_KEY).toString(), userType));
        payload.setExpiration(payload.getExpiration());
        return payload;
    }

    /**
     * 验证token是否过期
     *
     * @param token
     * @return
     */
    public static Boolean isAccessTokenExpired(String token, PublicKey publicKey) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
        final Claims claims = parserToken(token, publicKey);
        Date expiration = claims.getExpiration();
        if (expiration == null) {
            return true;
        }
        return expiration.before(new Date());
    }

}